#define _CRT_SECURE_NO_WARNINGS

#include <windows.h>

#include "FileProcessor.h"
#include "EofEvent.h"

using namespace std;

#include <boost/filesystem.hpp>
typedef boost::filesystem::path path;
typedef boost::filesystem::directory_iterator directory_iterator;


/** formats SYSTEMTIME to date-time string */
string formatTimeStamp(const SYSTEMTIME &st) {
	TCHAR buf[1024];
	::GetDateFormat(LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &st, NULL, buf, 1024);
	platform::XString str = buf;
	string res = str.getANSIString();

	res += ' ';
	::GetTimeFormat(LOCALE_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &st, NULL, buf, 1024);
	str = buf;
	res += str.getANSIString();

	return res;
}

/** formats time interval to be in form 0:13:10.463 */
string formatMsec(DWORD total) {
	DWORD remaining = total;

	DWORD msecs = remaining % 1000;
	remaining = (remaining - msecs) / 1000;
	DWORD secs = remaining % 60;
	remaining = (remaining - secs) / 60;
	DWORD mins = remaining % 60;
	remaining = (remaining - mins) / 60;
	DWORD hours = remaining;

	char buf[1024];
	sprintf(buf, "%d:%02d:%02d.%03d", hours, mins, secs, msecs);
	return buf;
}


#define PRINT_CHARS(o, c, i) for(unsigned int __i_ = 0; __i_ < i; __i_++) o << c;


/**
 * Prints general info based on file header
 */
void FileProcessor::printHeader(events::AbpaFileFormat &header, string &name) {
	path f(name);
	string shortName = f.leaf();
	_os << "+-"; PRINT_CHARS(_os, '-', shortName.length());
	_os << "-+\n| " << shortName << " |\n+-"; PRINT_CHARS(_os, '-', shortName.length());
	_os << "-+\n";

	_os << "Full path       : " << name << endl;
	if (!header.isValid()) {
		_os << "The header of the file is corrupted\n";
		return;
	}

	_os << "Version         : " << ((header.getVersion() & 0xff000000) >> 24) << '.' << (header.getVersion() & 0xffffff) << endl;
	_os << "Process name    : " << header.getProcessName().getANSIString() << endl;
	_os << "Started on      : " << formatTimeStamp(header.getTime()) << endl;
}

/**
 * If properly closed, each ABPA file must end with special
 * "FINISHED" event, containing summary info
 */
void FileProcessor::printEOFInfo(events::AbpaFileFormat &header, istream &in) {
	in.seekg(0-events::EofEvent::GetEventLength(), ios_base::end);
	try {
		events::BasicEvent *be = events::BasicEvent::fromStream(in);
		if (be == NULL || be->getType() != events::BasicEvent::EVENT_EOF) {
			_os << "The file was not closed properly\n";
			return;
		}
		events::EofEvent &eEof = static_cast<events::EofEvent&>(*be);
		_os << "Execution time  : " << formatMsec(eEof.getTickCount() - header.getTickCount()) << endl;
		_os << "Total events    : " << eEof.getNumOfEvents() << endl;

		delete be;
	} catch(...) {
		_os << "The file was not closed properly\n";
	}
}

/**
 * This function is used in "full-processing" mode, and it
 * dumps all events from the file (considering the filter, of
 * cause)
 */
void FileProcessor::dumpAllEvents(events::AbpaFileFormat &header, istream &in) {
	unsigned long total_events = 0;
	unsigned long skipped_events = 0;
	while (!in.eof()) {
		events::BasicEvent *be = events::BasicEvent::fromStream(in);
		if (be != NULL) {
			if (be->getType() == events::BasicEvent::EVENT_EOF) {
				// found a valid EOF event
				events::EofEvent &eEof = static_cast<events::EofEvent&>(*be);
				_os << "Execution time  : " << formatMsec(eEof.getTickCount() - header.getTickCount()) << endl;
				_os << "Total events    : " << total_events;
				if (skipped_events > 0)
					_os << " (" << skipped_events << " events were skipped)";
				_os << endl;
				delete be;
				return;
			} else {
				total_events ++;
				if (_etf.filterEvent(be->getType())) {
					_os << '[' << formatMsec(be->getTickCount() - header.getTickCount()) << "] ";
					be->writeDebugInfo(_os);
				} else {
					skipped_events ++;
				}
			}
			delete be;
		}
	}
	_os << "The file was not closed properly\n";
}


/*
 * This is the function to do most of the work
 * for this Whole program - it takes a single filename,
 * and performs all required processing of the output file
 */
void FileProcessor::process(string name) {
	try {
		// check whether passes the filters
		ifstream in(name.c_str(), ios_base::binary);
		if (!in.is_open()) {
			cerr << "ERROR: failed to read from \"" << name << "\"\n";
			return;
		}
		events::AbpaFileFormat header(in);

		// check the filter
		if (!_pnf.filterProcess(header.getProcessName().getANSIString()))
			return;

		// print the header
		printHeader(header, name);
		if (_headersOnly) {
			try {
				printEOFInfo(header, in);
			} catch(...) {
				_os << "The file was not closed properly";
			}
			return;
		}

		dumpAllEvents(header, in);
	} catch(...) {
		cerr << "ERROR: exception while processing file \"" << name << "\"\n";
		return;
	}
}
